package cn.com.bluemoon.daps.standard.service.impl;

import cn.com.bluemoon.daps.api.sys.RemoteSysDeptService;
import cn.com.bluemoon.daps.api.sys.RemoteSystemInfoService;
import cn.com.bluemoon.daps.common.domain.BaseModel;
import cn.com.bluemoon.daps.common.domain.ResultBean;
import cn.com.bluemoon.daps.common.enums.BmStatus;
import cn.com.bluemoon.daps.common.enums.StandardAuditStatus;
import cn.com.bluemoon.daps.common.exception.DapException;
import cn.com.bluemoon.daps.common.toolkit.BmAssetUtils;
import cn.com.bluemoon.daps.standard.client.DapSystemModuleFacade;
import cn.com.bluemoon.daps.standard.client.RemoteStandardIndexMetaDataServiceFacade;
import cn.com.bluemoon.daps.standard.dto.StandardQueryDto;
import cn.com.bluemoon.daps.standard.entity.*;
import cn.com.bluemoon.daps.standard.excel.pojo.IndexStandardExport;
import cn.com.bluemoon.daps.standard.excel.pojo.IndexStandardExportDto;
import cn.com.bluemoon.daps.standard.mapper.DapStandardIndexMapper;
import cn.com.bluemoon.daps.standard.mapper.DapSystemCategoryMapper;
import cn.com.bluemoon.daps.standard.service.*;
import cn.com.bluemoon.daps.standard.vo.IndexStandardRelateDwModelVo;
import cn.com.bluemoon.daps.standard.vo.IndexStandardVo;
import cn.com.bluemoon.daps.system.entity.DapSystemDepartment;
import cn.com.bluemoon.metadata.inter.dto.out.StandardIndexAssociatedFineReportVO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import reactor.util.function.Tuple2;
import reactor.util.function.Tuples;

import javax.annotation.Resource;
import java.io.Serializable;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * <p>
 * 指标标准表 服务实现类
 * </p>
 *
 * @author admin
 * @since 2020-08-03
 */
@Slf4j
@Service
@Transactional(rollbackFor = {DapException.class, Exception.class})
public class DapStandardIndexServiceImpl extends ServiceImpl<DapStandardIndexMapper, DapStandardIndex>
        implements DapStandardIndexService {

    /**
     * 匹配表名.字段名
     */
    private static final Pattern tableAndFieldMatch = Pattern.compile("[^.]+\\.[^.]+");
    @Resource
    private DapSystemSequenceGenerateService sequenceGenerateService;
    @Resource
    @Lazy
    private DapStandardAuditVersionMainService standardAuditVersionMainService;
    @Resource
    private DapSystemCategoryMapper dapSystemCategoryMapper;
    @Autowired
    private DapSystemCategoryService dapSystemCategoryService;
    @Autowired
    private RemoteSysDeptService dapSystemDepartmentService;
    @Autowired
    private DapSystemModuleFacade dapSystemModuleFacade;
    @Resource
    private DapStdIndexRelateMtbFieldService stdIndexRelateMtbFieldService;
    @Resource
    private RemoteSystemInfoService systemInfoService;
    @Autowired
    private DapStandardDimService dimService;
    @Autowired
    private RemoteStandardIndexMetaDataServiceFacade remoteStandardIndexMetaDataServiceFacade;

    /**
     * 编辑、新增
     */
    @Override
    public DapStandardIndex checkAndSaveOrUpdate(DapStandardIndex dapStandardIndex) {
        if (StringUtils.isNotBlank(dapStandardIndex.getId())) {
            DapStandardIndex sourceStandard = getByIdAsset(dapStandardIndex.getId(), "获取指标数据为空");
            // 新增编辑保留源状态
            dapStandardIndex.setStandardUniqueId(sourceStandard.getStandardUniqueId());
            dapStandardIndex.setPublishNewest(sourceStandard.getPublishNewest());
            dapStandardIndex.setPublishTime(sourceStandard.getPublishTime());
            // 生成编码序列号
            if (StringUtils.isBlank(dapStandardIndex.getIndexCode()) ||
                    !sourceStandard.getTheme().equalsIgnoreCase(dapStandardIndex.getTheme())) {
                String zbSq = sequenceGenerateService.getSeqByTypeAndTopicId("ZB", dapStandardIndex.getTheme());
                dapStandardIndex.setIndexCode(zbSq);
            }
        } else {
            // 生成编码序列号
            if (StringUtils.isBlank(dapStandardIndex.getIndexCode())) {
                String zbSq = sequenceGenerateService.getSeqByTypeAndTopicId("ZB", dapStandardIndex.getTheme());
                dapStandardIndex.setIndexCode(zbSq);
            }
        }
        // check 中英文唯一校验
        checkInsertOrUpdateModel(dapStandardIndex);
        boolean save = saveOrUpdate(dapStandardIndex);
        String standardIndexId = dapStandardIndex.getId();
        log.info("保存指标标准情况{},id:{}", save, standardIndexId);
        return dapStandardIndex;
    }

    @Override
    public void checkInsertOrUpdateModel(DapStandardIndex standardIndex) {
        LambdaQueryWrapper<DapStandardIndex> where = bmLambdaQuery()
                .ne(StringUtils.isNotBlank(standardIndex.getStandardUniqueId()), DapStandardIndex::getStandardUniqueId, standardIndex.getStandardUniqueId())
                .ne(StringUtils.isNotBlank(standardIndex.getId()), DapStandardIndex::getId, standardIndex.getId())
                .eq(DapStandardIndex::getIndexChinName, standardIndex.getIndexChinName())
                // 增加在业务线下中文名唯一
                .eq(standardIndex.getBizLineId() != null, DapStandardIndex::getBizLineId, standardIndex.getBizLineId());
        List<DapStandardIndex> list = list(where).stream().filter(d -> {
            // 需要去除流程中已被删除的标准数据后再进行判重
            String standardUniqueId = d.getStandardUniqueId();
            List<DapStandardAuditVersionMain> versions = standardAuditVersionMainService.findByStandardUniqueId(standardUniqueId);
            return versions.isEmpty() || !(versions.get(0).getModifyType() == 3 && StandardAuditStatus.已发布.equals(versions.get(0).getAuditStatus()));
        }).collect(Collectors.toList());
        BmAssetUtils.isTrue(list.isEmpty(), "指标中文名[" + standardIndex.getIndexChinName() + "]已存在");
        // 英文名
        /* // 已过时，不约束英文名称
        where = bmLambdaQuery()
                .ne(StringUtils.isNotBlank(standardIndex.getStandardUniqueId()), DapStandardIndex::getStandardUniqueId, standardIndex.getStandardUniqueId())
                .ne(StringUtils.isNotBlank(standardIndex.getId()), DapStandardIndex::getId, standardIndex.getId()).eq(DapStandardIndex::getIndexEngName, standardIndex.getIndexEngName());
        list = list(where).stream().filter(d -> {
            // 需要去除流程中已被删除的标准数据后再进行判重
            String standardUniqueId = d.getStandardUniqueId();
            List<DapStandardAuditVersionMain> versions = standardAuditVersionMainService.findByStandardUniqueId(standardUniqueId);
            return versions.isEmpty() || !(versions.get(0).getModifyType() == 3 && StandardAuditStatus.已发布.equals(versions.get(0).getAuditStatus()));
        }).collect(Collectors.toList());
        BmAssetUtils.isTrue(list.isEmpty(), "指标英文名[" + standardIndex.getIndexEngName() + "]已存在");*/
        // 主题id、一级分类id有效
        DapSystemCategory checkTopicId = dapSystemCategoryMapper.selectById(standardIndex.getTheme());
        BmAssetUtils.notNull(checkTopicId, "所选的主题不存在，请刷新重新选择");
        DapSystemCategory category1 = dapSystemCategoryMapper.selectById(standardIndex.getPrimarySort());
        BmAssetUtils.notNull(category1, "所选的一级分类不存在，请刷新重新选择");
        // 2021年5月25日12:02:54 新增需求：增加指标类型属性：原子指标、派生指标、计算指标； 影响属性：关联指标，原子指标没有关联指标
        if (standardIndex.getIndexType() != null && standardIndex.getIndexType() == 1) {
            BmAssetUtils.isTrue(StringUtils.isBlank(standardIndex.getRelatedIndex()), "原子指标不允许关联指标");
        }
    }

    @Override
    public void deleteStandardIndex(String standardId, boolean isLogicDel) {
        delete(standardId, isLogicDel, true);
    }

    /**
     * 多联级删除 指标标准-关联指标的有效数据
     * {@link DapStandardIndex#getRelatedIndex()} 为英文逗号分隔的字符串
     * 转换数组->遍历过滤需要删除的id
     */
    private void delete(String standardId, boolean isLogicDel, boolean delRelate) {
        DapStandardIndex standardIndex = getById(standardId);
        String standardUniqueId = Optional.ofNullable(standardIndex).orElseThrow(() -> new DapException("获取指标标准数据失败")).getStandardUniqueId();
        if (delRelate) {
            standardAuditVersionMainService.assetStandardPublished(standardUniqueId, "标准数据存在未发布版本，请处理后再删除");
//            多联级删除 指标标准-关联指标的有效数据(查询被关联的数据
            clearStdIndexRelateIndexes(standardUniqueId);
        }
        // 审批流程(去掉删除审批流程）2020年10月13日
        // 获取standardUniqueId的所有标准进行删除
        if (isLogicDel) {
            LambdaUpdateWrapper<DapStandardIndex> del = bmLambdaUpdate().set(DapStandardIndex::getBmStatus, BmStatus.DELETE).eq(DapStandardIndex::getStandardUniqueId, standardUniqueId);
            update(del);
        } else {
            LambdaUpdateWrapper<DapStandardIndex> del = bmLambdaUpdate().eq(DapStandardIndex::getStandardUniqueId, standardUniqueId);
            remove(del);
        }
        log.info("{}删除完成,id=>{}, 多联级删除=>{}", (isLogicDel) ? "逻辑" : "物理", standardId, delRelate);
    }

    /**
     * 多联级删除 指标标准-关联指标的有效数据
     *
     * @param standardUniqueId 标准唯一id
     */
    public void clearStdIndexRelateIndexes(String standardUniqueId) {
        List<DapStandardIndex> otherRelateIndexes = getBaseMapper().findOtherRelateIndexes(standardUniqueId);
        if (!otherRelateIndexes.isEmpty()) {
            List<DapStandardIndex> collect = otherRelateIndexes.stream().peek(i -> {
                String relatedIndex = i.getRelatedIndex();
                String[] relateIndexes = relatedIndex.split(",");
                String newRelateIndex = Arrays.stream(relateIndexes).filter(r -> !standardUniqueId.equals(r)).collect(Collectors.joining(","));
                i.setRelatedIndex(newRelateIndex);
            }).collect(Collectors.toList());
            updateBatchById(collect);
        }
    }

    /**
     * 本删除不对流程（多版本进行删除）
     *
     * @param standardId biaozhun
     * @param isLogicDel 逻辑
     */
    @Override
    public void deleteSingleStandardIndex(String standardId, boolean isLogicDel) {
        delete(standardId, isLogicDel, false);
    }

    @Override
    public List<IndexStandardVo.ReportInfo> relateReportInfo(String id) {
        DapStandardIndex standardIndex = getById(id);
        if (standardIndex != null && standardIndex.getDataWarehouseModel() != null
                && standardIndex.getDataWarehouseModel().indexOf(".") > 0) {
            String[] tableField = standardIndex.getDataWarehouseModel().split("\\.");
            List<StandardIndexAssociatedFineReportVO> res = remoteStandardIndexMetaDataServiceFacade.
                    standardIndexAssociatedFineReportVOS(tableField[0], tableField[1]);
            return res.stream().map(s -> {
                IndexStandardVo.ReportInfo reportInfo = new IndexStandardVo.ReportInfo();
                BeanUtils.copyProperties(s, reportInfo);
                return reportInfo;
            }).collect(Collectors.toList());
        }

        return Collections.emptyList();
    }

    @Override
    public Tuple2<Set<String>, Set<String>> dbPublishStandardNameAndEngName() {
        LambdaQueryWrapper<DapStandardIndex> where = Wrappers.<DapStandardIndex>lambdaQuery()
//                .eq(DapStandardIndex::getPublishNewest, Boolean.TRUE)
                .eq(BaseModel::getBmStatus, BmStatus.ON);
        List<DapStandardIndex> list = list(where);
        Set<String> t1 = list.stream().map(DapStandardIndex::getIndexChinName).collect(Collectors.toSet());
        Set<String> t2 = list.stream().map(DapStandardIndex::getIndexEngName).collect(Collectors.toSet());
        return Tuples.of(t1, t2);
    }

    @Override
    public Collection<DapStandardIndex> getPublishStandardIndexes(String... outOfStandardUniqueId) {
        LambdaQueryWrapper<DapStandardIndex> where = bmLambdaQuery().eq(DapStandardIndex::getPublishNewest, 1).eq(BaseModel::getBmStatus, BmStatus.ON);
        List<DapStandardIndex> dapStandardIndices = getBaseMapper().selectList(where)
                .stream()
                .filter(s -> StringUtils.isNotBlank(s.getStandardUniqueId()))
                .collect(Collectors.toMap(d -> d, v -> v, (a, b) -> b)).keySet()
                .stream().sorted(new PublishStandardIndexComparator()).collect(Collectors.toList());
        List<String> outOf;
        if (outOfStandardUniqueId == null || (outOf = Arrays.asList(outOfStandardUniqueId)).isEmpty()) {
            return dapStandardIndices;
        }
        return dapStandardIndices.stream()
                .filter(s -> !outOf.contains(s.getStandardUniqueId()))
                .collect(Collectors.toList());
    }

    @Override
    public void transferDicInfo(DapStandardIndex sourceStandardIndex, IndexStandardVo targetStandardIndex) {
        if (sourceStandardIndex == null) {
            return;
        }
        try {
            CompletableFuture.runAsync(() -> {
                if (StringUtils.isNotBlank(sourceStandardIndex.getTheme())) {
                    DapSystemCategory systemCategory = dapSystemCategoryService.getById(sourceStandardIndex.getTheme());
                    Optional.ofNullable(systemCategory).ifPresent(s -> targetStandardIndex.setTopicName(s.getCodeName()));
                }
            }).thenRunAsync(() -> {
                if (StringUtils.isNotBlank(sourceStandardIndex.getPrimarySort())) {
                    DapSystemCategory systemCategory = dapSystemCategoryService.getById(sourceStandardIndex.getPrimarySort());
                    Optional.ofNullable(systemCategory).ifPresent(s -> targetStandardIndex.setFirstCategoryName(s.getCodeName()));
                }
            }).thenRunAsync(() -> {
                if (StringUtils.isNotBlank(sourceStandardIndex.getSecondarySort())) {
                    DapSystemCategory systemCategory = dapSystemCategoryService.getById(sourceStandardIndex.getSecondarySort());
                    Optional.ofNullable(systemCategory).ifPresent(s -> targetStandardIndex.setSecondCategoryName(s.getCodeName()));
                }
            }).thenRunAsync(() -> {
                if (StringUtils.isNotBlank(sourceStandardIndex.getThirdSort())) {
                    DapSystemCategory systemCategory = dapSystemCategoryService.getById(sourceStandardIndex.getThirdSort());
                    Optional.ofNullable(systemCategory).ifPresent(s -> targetStandardIndex.setThirdCategoryName(s.getCodeName()));
                }
            }).thenRunAsync(() -> {
                if (StringUtils.isNotBlank(sourceStandardIndex.getManagementDepartmentId())) {
                    ResultBean<DapSystemDepartment> dtoResultBean = dapSystemDepartmentService.getById(sourceStandardIndex.getManagementDepartmentId());
                    Optional.ofNullable(dtoResultBean.getContent()).ifPresent(s -> targetStandardIndex.setDepartmentName(dtoResultBean.getContent().getDepartName()));
                }
            }).thenRunAsync(() -> {
                Optional.ofNullable(sourceStandardIndex.getBizLineId())
                        .map(bizLineId -> {
                            return dapSystemModuleFacade.getBizLineInfoById(bizLineId);
                        })
                        .ifPresent(bizLine -> {
                            targetStandardIndex.setBizLine(bizLine.getCodeName());
                        });
            }).thenRunAsync(() -> {
                Optional.ofNullable(sourceStandardIndex.getDataSourceSystem())
                        .flatMap(s -> systemInfoService.getById(s))
                        .ifPresent(a -> targetStandardIndex.setDataSourceSystemName(a.getName()));
            }).thenRunAsync(() -> {
                final String dimIds = sourceStandardIndex.getAssociationDimension();
                Optional.ofNullable(dimIds)
                        .filter(StringUtils::isNotBlank)
                        .map(d -> Arrays.stream(d.split(",")).collect(Collectors.toList()))
                        .filter(l -> !l.isEmpty())
                        .ifPresent(l -> {
                            Map<String, String> idAndNames = dimService.listByIds(l).stream().collect(Collectors.toMap(DapStandardDim::getId, DapStandardDim::getCodeName));
                            String dimNames = Arrays.stream(dimIds.split(",")).map(id -> idAndNames.getOrDefault(id, id)).collect(Collectors.joining(","));
                            targetStandardIndex.setAssociationDimensionNames(dimNames);
                        });
            }).thenRunAsync(() -> {
                Optional.ofNullable(sourceStandardIndex.getRelatedIndex())
                        .filter(StringUtils::isNotBlank)
                        .map(d -> Arrays.stream(d.split(",")).collect(Collectors.toList()))
                        .filter(l -> !l.isEmpty())
                        .ifPresent(l -> {
                            List<DapStandardIndex> indices = list(bmLambdaQuery().eq(DapStandardIndex::getPublishNewest, true).in(DapStandardIndex::getStandardUniqueId, l));
                            String indexNames = indices.stream().map(DapStandardIndex::getIndexChinName).collect(Collectors.joining(","));
                            targetStandardIndex.setRelatedIndexNames(indexNames);
                        });
            }).get();
        } catch (InterruptedException | ExecutionException e) {
            log.error("指标转换异常", e);
        }
    }

    @Override
    public Map<String, String> dbPublishStandardNameAndStdUniqueIds() {
        List<DapStandardIndex> list = list(bmLambdaQuery().eq(DapStandardIndex::getPublishNewest, Boolean.TRUE));
        return list.stream().collect(Collectors.toMap(DapStandardIndex::getIndexChinName, DapStandardIndex::getStandardUniqueId, (a, b) -> b));
    }

    @Override
    public String relateDwModelTableInfo(IndexStandardRelateDwModelVo dwModelVo) {
        if (!dwModelVo.validParam()) {
            log.info("该请求标准没有（重新）进行关联模型表信息，忽略");
            return null;
        }
        DapStandardIndex stdIndex = getByIdAsset(dwModelVo.getStandardId(), "标准不存在，请检查");
        final String standardUniqueId = stdIndex.getStandardUniqueId();
        LambdaQueryWrapper<DapStdIndexRelateMtbField> eq = stdIndexRelateMtbFieldService.bmLambdaQuery()
                .eq(DapStdIndexRelateMtbField::getStandardUniqueId, standardUniqueId);
        int count = stdIndexRelateMtbFieldService.count(eq);
        if (count > 0) {
            boolean remove = stdIndexRelateMtbFieldService.remove(eq);
            log.info("删除历史关系，status:{}", remove);
        }
        // 创建关系
        DapStdIndexRelateMtbField mtbField = new DapStdIndexRelateMtbField();
        mtbField.setStandardUniqueId(standardUniqueId);
        mtbField.setModelId(dwModelVo.getDwModelId());
        mtbField.setModelTablePId(dwModelVo.getDwTableId());
        mtbField.setModelTableFieldPId(dwModelVo.getDwTableFieldId());
        mtbField.setBmStatus(BmStatus.ON);
        boolean save = stdIndexRelateMtbFieldService.save(mtbField);
        log.info("建立指标标准与模型表关系,{}, status:{}", mtbField, save);
        return mtbField.getId();
    }

    @Override
    public List<IndexStandardExport> exportData(StandardQueryDto standardQueryDto) {
        if (standardQueryDto == null) {
            return exportData();
        }
        List<IndexStandardExportDto> list = getBaseMapper().queryPublishIndexStandard(standardQueryDto);
        return indexStandardExportProcess(list);
    }

    @Override
    public List<IndexStandardExport> exportData() {
        List<IndexStandardExportDto> list = getBaseMapper().queryAllPublishIndexStandard();
        return indexStandardExportProcess(list);
    }

    private List<IndexStandardExport> indexStandardExportProcess(List<IndexStandardExportDto> list) {
        Map<String, IndexStandardExportDto> uniqueIdAndIndexInfo = list.parallelStream().collect(Collectors.toMap(IndexStandardExportDto::getStandardUniqueId, a -> a, (a, b) -> b));
        Map<String, DapStandardDim> idAndDimInfo = dimService.list(dimService.bmLambdaQuery()).stream().collect(Collectors.toMap(DapStandardDim::getId, a -> a, (a, b) -> b));
        return list.parallelStream().map(d -> {
            // 模型表与字段
            if (StringUtils.isNotBlank(d.getWhModel()) && tableAndFieldMatch.matcher(d.getWhModel()).matches()) {
                String[] whModel = d.getWhModel().split("\\.");
                d.setWhModel(whModel[0]);
                d.setWhModelTable(whModel[1]);
            }
            // 关联指标
            String relateIndexes = d.getRelateIndexIds();
            if (StringUtils.isNotBlank(relateIndexes)) {
                String indexNames = Arrays.stream(relateIndexes.split(",")).map(id -> {
                    return uniqueIdAndIndexInfo.get(id) != null ? uniqueIdAndIndexInfo.get(id).getIndexName() : id;
                }).collect(Collectors.joining(","));
                d.setRelateIndexes(indexNames);
            }
            // 关联维度
            String relateDimIds = d.getRelateDimIds();
            if (StringUtils.isNotBlank(relateDimIds)) {
                String dimNames = Arrays.stream(relateDimIds.split(",")).map(id -> {
                    return idAndDimInfo.get(id) != null ? idAndDimInfo.get(id).getCodeName() : id;
                }).collect(Collectors.joining(","));
                d.setRelateIndexes(dimNames);
            }
            return (IndexStandardExport) d;
        }).collect(Collectors.toList());
    }

    /**
     * 针对已发布的数据进行排序
     */
    public static class PublishStandardIndexComparator implements Comparator<DapStandardIndex>, Serializable {
        private static final long serialVersionUID = 1L;

        @Override
        public int compare(DapStandardIndex o1, DapStandardIndex o2) {
            if (o1 == null || o2 == null) {
                return 0;
            }
            if (o1.getPublishTime().isEqual(o2.getPublishTime())) {
                return 0;
            }
            return o1.getPublishTime().isAfter(o2.getPublishTime()) ? 1 : -1;
        }
    }

}
